﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Device.Location;
using Microsoft.Phone.Reactive;
using System.Threading;
using System.Collections.Generic;
using System.Windows.Threading;
using ChiantiPhoneListApp.ChiantiServiceReference5;

namespace ChiantiPhoneListApp.Services
{
    public class LocationService
    {
        GeoCoordinateWatcher watcher;
        bool useEmulation = true;

        private OnLocationChange _onLocationChange;
        private Position _currentLatitude;

        public Position CurrentLatitude
        {
            get { return _currentLatitude; }
            set { _currentLatitude = value; }
        }

        private Position _currentLongitude;

        public Position CurrentLongitude
        {
            get { return _currentLongitude; }
            set { _currentLongitude = value; }
        }
        public OnLocationChange OnLocationChange
        {
            get { return _onLocationChange; }
            set { _onLocationChange = value; }
        }

        public LocationService()
        {
            this.OnLocationChange += del_OnLocationChange;

            // First, handle the case where emulation is not being used.
            if (!useEmulation)
            {
                // Initialize the GeoCoordinateWatcher.
                watcher = new GeoCoordinateWatcher();

                // Reactive Extensions uses Observable sequences to represent data streams.
                // Create an Observable sequence from the event stream using the FromEvent method.
                // This method uses the .NET Generic syntax to specify the type of event args for the event.
                // The parameters to the method are the add and remove handlers of the GeoCoordinateWatcher object.

                IObservable<IEvent<GeoPositionChangedEventArgs<GeoCoordinate>>> positionEventAsObservable =
                    Observable.FromEvent<GeoPositionChangedEventArgs<GeoCoordinate>>(
                    ev => watcher.PositionChanged += ev,
                    ev => watcher.PositionChanged -= ev);

                // Subscribe to the observable data stream. You can use the same event handler as if you 
                // were using the GeoCoordinateWatcher.PositionChanged event directly.
                var positionSubscription = positionEventAsObservable.Subscribe(
                    args => OnLocationChange(args.Sender, args.EventArgs));

                // Start the GeoCoordinateWatcher to begin receiving data from the Location Service.
                watcher.Start();
            }
            else
            {
                // Start the thread on which emulated location data is generated.
                // The method StartEmulation is defined next.
                Thread emulationThread = new Thread(StartEmulation);
                emulationThread.Start();
            }
        }

        private void del_OnLocationChange(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
        {
            this._currentLatitude = new Position(e.Position.Location.Latitude);
            this._currentLongitude = new Position(e.Position.Location.Longitude);
        }

        private void StartEmulation()
        {
            // EmulatePositionChangedEvents returns an IEnumerable object.
            // Convert this to an Observable sequence.
            var position = EmulatePositionChangedEvents().ToObservable();

            // Subscribe to the Observable sequence.
            // Use null for the sender parameter to the event handler.
            position.Subscribe(evt => OnLocationChange(null, evt));
        }

        static IEnumerable<GeoPositionChangedEventArgs<GeoCoordinate>> EmulatePositionChangedEvents()
        {
            // Create a Random object to create random numbers.
            Random random = new Random();

            // Loop infinitely.
            for (; ; )
            {
                // Pause for 100 milliseconds in each loop.
                Thread.Sleep(random.Next(100));

                // Generate a random latitude and longitude. You could also load position data from a file or
                // generate the data from user input.
                double latitude = Constants.DUMMY_LATITUDE;
                double longitude = Constants.DUMMY_LONGITUDE;

                // Use yield to return a new instance of the GeoPositionChangedEventArgs class that is exposed
                // through the IEnumerable interface.
                yield return new GeoPositionChangedEventArgs<GeoCoordinate>(
                  new GeoPosition<GeoCoordinate>(DateTimeOffset.Now, new GeoCoordinate(latitude, longitude)));

            }

        }
        public ChiantiServiceReference5.Position GetCurrentUserLocation()
        {
            return null;
        }


        internal ChiantiServiceReference5.Position GetCurrentUserLatitude()
        {
            return new ChiantiServiceReference5.Position(Constants.DUMMY_LATITUDE);
        }

        internal ChiantiServiceReference5.Position GetCurrentUserLongitude()
        {
            return new ChiantiServiceReference5.Position(Constants.DUMMY_LONGITUDE);
        }




        internal double GetDistanceInMeters(double long1, double lat1, double long2, double lat2)
        {
            double dLatInRad = (lat2 - lat1) * (Math.PI / 180);
            double dLongInRad = (long2 - long1) * (Math.PI / 180);

            double a = Math.Sin(dLatInRad / 2) * Math.Sin(dLatInRad / 2) +
                            Math.Cos(lat1 * Math.PI / 180) * Math.Cos(lat2 * Math.PI / 180) *
                            Math.Sin(dLongInRad / 2) * Math.Sin(dLongInRad / 2);

            double c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
            double result = Constants.RADIUS_OF_EARTH_METERS * c;
            return result;
        }

        internal  double GetDistanceInMeters(ChiantiServiceReference5.Position longitude1, ChiantiServiceReference5.Position latitude1, ChiantiServiceReference5.Position longitude2, ChiantiServiceReference5.Position latitude2)
        {
            double long1 = longitude1.ToDecimal();
            double lat1 = latitude1.ToDecimal();
            double long2 = longitude2.ToDecimal();
            double lat2 = latitude2.ToDecimal();
            return GetDistanceInMeters(long1, lat1, long2, lat2);


        }
    }
}
